package edu.kit.ksri.db.mturk.utils.messaging;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import org.apache.log4j.Logger;

import com.amazonaws.mturk.requester.Assignment;
import com.amazonaws.mturk.requester.HIT;

import edu.kit.ksri.db.mturk.connectionhandlers.AbstractHITManager;
import edu.kit.ksri.db.mturk.plugins.correctness.AbstractCSP1AndWMVImplementation;
import edu.kit.ksri.db.mturk.plugins.correctness.answers.AbstractAssignmentAnswer;
import edu.kit.ksri.db.mturk.var.ConnectionMode;
import edu.kit.ksri.db.mturk.var.Icd;
import edu.kit.ksri.db.mturk.var.StaticInformation;

/**
 * Copyright (c) 2010-2011 Karlsruhe Institute of Technology (KIT), Germany
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * 
 * --------------------------------------------------------------------------
 * 
 * Software: CSP/WMV tool for dynamic quality management of microtasks
 * http://www.ksri.kit.edu/Upload/Users/PeopleClouds/HCOMP2011_An_extendable_toolkit_for_managing_quality_of_human-based_eServices.pdf
 * 
 * Organization: Karlsruhe Institute of Technology (KIT), Germany
 * http://www.kit.edu
 * 
 * Project: PeopleClouds project of the Karlsruhe Service Research Institute(KSRI)
 * http://www.ksri.kit.edu/Default.aspx?PageId=818
 * 
 * Contributing authors: David Bermbach, Sandra Rath, Pascal Wichmann, Robert Kern
 * 
 * ---------------------------------------------------------------------------
 * 
 * Purpose: This class periodically connects to the MTurk site, retrieves all new
 * assignments and puts them into a queue which is then worked on by
 * implementations of {@link AbstractCSP1AndWMVImplementation}.
 */
public class HITResultFetcher implements Runnable {

	private static Logger basiclog = Logger.getLogger("basic." + HITResultFetcher.class);
	
	/** singleton instance */
	private static final HITResultFetcher instance = new HITResultFetcher();

	/**
	 * the assignment ids. every retrieved assignment is added to this set so
	 * that no assignment will be forwarded more than once.
	 */
	private HashSet<String> retrievedAssignments = new HashSet<String>();

	/** stores all hit ids found on mturk, is updated every tenth time. */
	private String[] hitids = new String[0];

	/** count the number of retrieved HITs */
	private static int counter = 0;

	private HITResultFetcher() {
	}

	/**
	 * 
	 * @return the {@link HITResultFetcher} singleton
	 */
	public static HITResultFetcher getInstance() {
		return instance;
	}

	/**
	 * when this variable is set to true, the {@link HITResultFetcher} stops
	 * running and terminates operations.
	 */
	private boolean running = true;

	/** is set to true once all active processes have terminated. */
	private boolean terminated = false;

	public void run() {
		// every 10th time the method checks whether new hits have been
		// submitted
		int recheckCounter = -1;
		while (running) {
			recheckCounter++;
			try {
				Thread.sleep(StaticInformation.millisBetweenMTurkPolling);
			} catch (InterruptedException e) {
				basiclog.warn("Caught InterruptedException: " + e);
			}
			if (recheckCounter % 10 == 0) {
				// it's time to get new hit ids
				ConnectionMode connectionMode = StaticInformation.scenario.getConnectionMode();
				String[] temp;
				if(connectionMode == ConnectionMode.LIVE || connectionMode == ConnectionMode.CORRSIMINPUT)
				{
					HIT[] hits = AbstractHITManager.getInstance().getAllHITsForHITType();
					temp = new String[hits.length];
					int counter = 0;
					for(HIT h : hits)
					{
						temp[counter++] = h.getHITId();
					}
				}
				else{
					temp = AbstractHITManager.getInstance().getAllHITIDs();
					//temp = AbstractHITManager.getInstance().getAllPublishedHITIDs();
				}

				if (temp.length == 0 && hitids.length > 0) {
					basiclog.info("Skipping update due to connection error.");
				} else {
					hitids = temp;
				}
			}
			for (String s : hitids) {				
				Assignment[] a = AbstractHITManager.getInstance()
						.getAssignmentsForHITID(s);
				if (a.length != 0) {
					// check whether we have already retrieved a particular
					// assignment
					ArrayList<Assignment> newAssignments = new ArrayList<Assignment>();
					for (Assignment aa : a) {
						if (retrievedAssignments.contains(aa.getAssignmentId())){
							basiclog.debug("Assignment " + aa.getAssignmentId() + "is not new!");
							continue;
						}
							
						retrievedAssignments.add(aa.getAssignmentId());
						newAssignments.add(aa);
						counter++;
					}
					
					//basiclog.debug("Retrieved Assignments: " + retrievedAssignments);

					Result r = new Result(s, newAssignments
							.toArray(new Assignment[newAssignments.size()]));
					ResultQueue.getInstance().enqueue(r);
					
					if (newAssignments.size() > 0)
						basiclog.info("Enqueued " + newAssignments.size()
								+ " results for HIT ID " + s + " (" + counter
								+ " assignments in total for all HITs).");
					// approve all new assignments
					for (Assignment aa : newAssignments) {

						boolean b = AbstractHITManager
								.getInstance()
								.approveAssignment(aa.getAssignmentId(),
										"Keep up the good work and you can continue to work for us!");
						basiclog.info(b ? "Assignment " + aa.getAssignmentId()
								+ " approved" : "Approving assignment "
								+ aa.getAssignmentId() + " failed.");
						
						// Log the answer(s) with the basic logger
						HashMap<String, String> answers = AbstractAssignmentAnswer.parseAnswer(aa.getAnswer());
						
						if(StaticInformation.scenario instanceof Icd){
							basiclog.info("Answer with assignment ID " + aa.getAssignmentId() + " from worker " + aa.getWorkerId() + " is:");
							
							//Do this for every label (equals atomic answer) we have
							for(int j = 1; j <= Icd.maxNumberOfLabels; j++){
	
								String code = answers.get("code"+String.valueOf(j));
								String flag1 = answers.get("code"+String.valueOf(j)+"flag1");
								String flag2 = answers.get("code"+String.valueOf(j)+"flag2");
								String flag3 = answers.get("code"+String.valueOf(j)+"flag3");
								String flag4 = answers.get("code"+String.valueOf(j)+"flag4");
								
								//change false|true answers to true
								if (flag1.contains("true")) {
									flag1 = "true";				
								}
								if (flag2.contains("true")) {
									flag2 = "true";				
								}
								if (flag3.contains("true")) {
									flag3 = "true";				
								}
								if (flag4.contains("true")) {
									flag4 = "true";				
								}

								String label = code+" "+flag1+" "+flag2+" "+flag3+" "+flag4;
								label = label.trim();
								
								//Check if no code was entered (worker did not use this label)
								if(!code.equals(""))
									basiclog.info("- Label " + j + " is \"" + label +"\".");
							}
							String comment = answers.get("comment");
							if(!comment.equals(""))
								basiclog.info("Worker entered this comment: \"" + comment + "\"");
							basiclog.info("----------------------------------------------------------");
						}
						else{
							Set<String> answerKeySet = answers.keySet();
							for(String answerKey : answerKeySet){
								String answer = answers.get(answerKey);
								basiclog.info("Given answer with assignment ID " + aa.getAssignmentId() + " from worker " + aa.getWorkerId() + " is \"" + answer + "\".");
								basiclog.info("----------------------------------------------------------");
							}
						}
					}
				} else {
					// basiclog.warn("The Assignment [] was empty...");
				}
			}

		}
		basiclog.info("HITResultFetcher terminated.");
		terminated = true;
	}

	/**
	 * makes the run() method terminate without exceptions or undefined states
	 */
	public void quitHitResultFetcher() {
		running = false;
	}

	/**
	 * starts a new {@link HITResultFetcher} thread
	 * 
	 */
	public void restartHitResultFetcher() {
		running = true;
		new Thread(this).start();
	}

	/**
	 * @return the running
	 */
	public boolean isRunning() {
		return this.running;
	}

	/**
	 * @param running
	 *            the running to set
	 */
	public void setRunning(boolean running) {
		this.running = running;
	}

	/**
	 * @return the terminated
	 */
	public boolean isTerminated() {
		return this.terminated;
	}

	/**
	 * @param terminated the terminated to set
	 */
	public void setTerminated(boolean terminated) {
		this.terminated = terminated;
	}

}
